/*
 * @Author: OreoWang
 * @Email: ihc523@163.com
 * @Date: 2020-06-01 12:05:23
 * @LastEditors: OreoWang
 * @LastEditTime: 2022-08-11 10:27:46
 * @Description: 模型视图控制器
 */

import { _decorator, Component, Node, EventTouch, EventHandler, js, Slider, ToggleContainer, Toggle, Button } from "cc";
import ViewNode from "./ViewNode";
import ViewModel, { ViewData } from "./ViewModel";
import { logger } from "./ViewLogger";

const {ccclass, property, disallowMultiple} = _decorator;                                                                            

@ccclass("ViewController")
@disallowMultiple
export default class ViewController<V extends ViewNode = ViewNode, M extends ViewModel = ViewModel, D extends ViewData = ViewData> extends Component {
    @property(ViewNode)
    view: V = null!;
    data: D = null!;
    model: M = null!;

    //监听事件时是否清理旧事件
    protected _clearEvents = true;

    //当前类的所有方法名
    private _allMethods: Set<string> = null!;

    /**
     * 子类覆盖实现
     */
    __preload(){
        throw new Error("子类覆盖此方法，实例化 data 和 model .");

        // this.data = new ViewData() as D;
        // this.model = new ViewModel(this, this.view, this.data) as M;
    }
    
    onLoad(){
    }
    onDestroy(){
        this.model?.destroy();
    }
    
    /**
     * listen 事件监听的默认回调（子类重写）
     * @param event 
     * @param data 
     */
    onEventHandler(event: /*EventTouch*/ any, data?: string){
        logger.warn("ViewController.onEventHandler", event, data);
    }
    
    /**
     * 在当前脚本上监听交互事件。如果不传 handler，则内部默认调用onEventHandler
     * @param handler 
     * @param component 
     * @param data 
     */
     _listen(component: Component, data?: string|null, handler?: Function): any{
        let target = this;
        let function_name = "onEventHandler";
        if(handler){
            let methods = this._getAllMethods();
            for (const key of methods) {
                //@ts-ignore
                const value = this[key];
                if(typeof value == "function" && value == handler){
                    function_name = key;
                    break;
                }
            }
        }
        
        var eventHandler = new EventHandler()
        eventHandler.target = target.node;
        eventHandler.component = js.getClassName(target);
        eventHandler.customEventData = data!;
        eventHandler.handler = function_name;
        this._pushEvent(component, eventHandler);
    }

    /**
     * 自定义处理组件事件（子类需要时重写）
     * @param component 
     * @param eventHandler 
     * @returns 
     */
    _onPushEvent(component: Component, eventHandler: EventHandler){
        return false;
    }

    /**
     * 清理旧事件
     * @param array 
     */
     _checkAndClearEvents(events: any[]){
        if(events?.length > 0){
            if(this._clearEvents){
                events.length = 0;
            }
            else{
                logger.error("旧事件未清理：", events.length, events);
            }
        }
    }

    /**
     * 获取当前类的所有方法名
     * @returns 
     */
    _getAllMethods(){
        if(this._allMethods) return this._allMethods;

        this._allMethods = new Set<string>();
        let properties = new Set<string>();
        let obj: any = this;
        let currentObj = obj;
        do {
            Object.getOwnPropertyNames(currentObj).map((item: string) => properties.add(item))
        } while ((currentObj = Object.getPrototypeOf(currentObj)))
        
        properties.forEach((key)=>{
            if(typeof obj[key] === 'function'){
                this._allMethods.add(key);
            }
        })

        return this._allMethods;
    }

    /**
     * 自定义处理组件事件
     * @param component 
     * @param eventHandler 
     */
    _pushEvent(component: Component, eventHandler: EventHandler){
        if(component instanceof Slider){
            this._checkAndClearEvents(component.slideEvents);
            component.slideEvents.push(eventHandler);
        }
        else if(component instanceof ToggleContainer){
            this._checkAndClearEvents(component.checkEvents);
            component.checkEvents.push(eventHandler);
        }
        else if(component instanceof Toggle){
            this._checkAndClearEvents(component.checkEvents);
            component.checkEvents.push(eventHandler);
        }
        else if(component instanceof Button){
            this._checkAndClearEvents(component.clickEvents);
            component.clickEvents.push(eventHandler);
        }
        else{
            let handled = this._onPushEvent(component, eventHandler);
            if(!handled){
                logger.error("组件未处理：", component);
            }
        }
    }
}